home *** CD-ROM | disk | FTP | other *** search
- /*
- File: NoiseMaker.c
-
- Contains: Sample sound output component
-
- Written by: Kip Olson
-
- Copyright: © 1994 by Apple Computer, Inc.
- */
-
- #include <Memory.h>
- #include <Errors.h>
- #include <SoundInput.h>
- #include <Components.h>
- #include <GestaltEqu.h>
-
- #include "Sound.h"
- #include "SoundComponents.h"
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Constants
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- #define kDelegateComponentCall ((ComponentRoutine) -1L) // flag that selector should be delegated instead of called
-
- #define kNoiseMakerVersion 0x00010000 // version for this sound component
- #define kRequiredSndMgrMajorRev 3 // Sound Manager version required to run this component
-
- // hardware settings
-
- #define kSampleSizesCount 2
- #define k8BitSamples 8 // 8-bit samples
- #define k16BitSamples 16 // 16-bit samples
-
- #define kSampleRatesCount 3
- #define kSupportsSampleRateRange false // set to true to use sample rate range
- #define kSampleRateMin 0x2B110000 // sample rate min = 11.025 kHz
- #define kSampleRateMax 0xAC440000 // sample rate max = 44.100 kHz
-
- #define kSampleRate44 0xAC440000 // 44.1000 kHz rate
- #define kSampleRate22 0x56220000 // 22.050 kHz rate
- #define kSampleRate11 0x2B110000 // 11.025 kHz rate
-
- #define kChannelsCount 2
- #define kNumChannelsMono 1 // mono
- #define kNumChannelsStereo 2 // stereo
-
- #define kInterruptBufferSamples 512 // size of interrupt buffer in samples
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Data Structures
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- /* Data structure passed to some GetInfo calls */
-
- typedef struct {
- short count;
- Handle handle;
- } HandleList, *HandleListPtr;
-
- /* Preferences data structure. A handle containing this data structure is stored in the
- Sound Preferences file and is loaded into the sound component refcon when it is opened. */
-
- typedef struct {
- UnsignedFixed sampleRate;
- short sampleSize;
- short numChannels;
- unsigned long volume;
- } PreferencesRecord, *PreferencesPtr, **PreferencesHandle;
-
- /* Sound component globals */
-
- typedef struct {
-
- // these are general purpose variables that every sound component will need
- ComponentInstance self; // ourselves
- ComponentInstance sourceComponent; // component to call when hardware needs more data
- SoundComponentDataPtr sourceDataPtr; // pointer to source data structure
- Handle globalsHandle; // handle to component globals
- Boolean inSystemHeap; // true if component loaded in system heap, false if in app heap
- Boolean prefsChanged; // true if preferences have changed
- PreferencesHandle prefsHandle; // global preferences
-
- // these are variables specific to this implementation
- SoundComponentData hwSettings; // current hardware settings
- unsigned long hwVolume; // current hardware volume
- Boolean hwInterruptsOn; // true if sound is playing
- Boolean hwInitialized; // true if hardware was initialized by __InitOutputDevice
- SndChannelPtr sndChan; // sound channel in use
- }
- GlobalsRecord, *GlobalsPtr;
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Prototypes
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- #ifdef THINK_C
- pascal ComponentResult main(ComponentParameters *params, GlobalsPtr globals);
- #else
- pascal ComponentResult NoiseMaker(ComponentParameters *params, GlobalsPtr globals);
- #endif
-
- ComponentRoutine GetComponentRoutine(short selector);
- pascal ComponentResult __ComponentRegister(void *unused1);
- pascal ComponentResult __ComponentVersion(void *unused1);
- pascal ComponentResult __ComponentCanDo(void *unused1, short selector);
- pascal ComponentResult __ComponentOpen(void *unused1, ComponentInstance self);
- pascal ComponentResult __ComponentClose(GlobalsPtr globals, ComponentInstance self);
- pascal ComponentResult __InitOutputDevice(GlobalsPtr globals, long actions);
- pascal ComponentResult __GetInfo(GlobalsPtr globals, SoundSource sourceID, OSType selector, void *infoPtr);
- pascal ComponentResult __SetInfo(GlobalsPtr globals, SoundSource sourceID, OSType selector, void *infoPtr);
- pascal ComponentResult __StartSource(GlobalsPtr globals, short count, SoundSource *sources);
- pascal ComponentResult __PlaySourceBuffer(GlobalsPtr globals, SoundSource sourceID, SoundParamBlockPtr pb, long actions);
-
- Handle NewHandleLockClear(long len, Boolean inSystemHeap);
- PreferencesHandle GetPreferences(ComponentInstance self, Boolean inSystemHeap);
- void SavePreferences(ComponentInstance self, PreferencesHandle prefsHandle);
- OSErr SetupHardware(GlobalsPtr globals);
- void ReleaseHardware(GlobalsPtr globals);
- OSErr StartHardware(GlobalsPtr globals);
- void StopHardware(GlobalsPtr globals);
- void SuspendHardware(GlobalsPtr globals);
- void ResumeHardware(GlobalsPtr globals);
- OSErr SetHardwareVolume(GlobalsPtr globals, unsigned long volume);
- pascal void InterruptRoutine(SndChannelPtr chan, SndCommand *cmd);
- SoundComponentDataPtr GetMoreSource(GlobalsPtr globals);
- void CopySamplesToHardware(GlobalsPtr globals, SoundComponentDataPtr sourceDataPtr);
-
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Main Component Entry Point
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- /* =======================================================================================
- NoiseMaker
-
- The function of this routine is to dispatch to the appropriate component method. It first
- calls finds the address of the method to dispatch to using the selector provided in the
- what field of the parameter block. If the address is -1L, then this selector should
- be delegated. If the address is nil, this selector is not supported.
- ======================================================================================= */
-
- #ifdef THINK_C
- pascal ComponentResult main(ComponentParameters *params, GlobalsPtr globals)
- #else
- pascal ComponentResult NoiseMaker(ComponentParameters *params, GlobalsPtr globals)
- #endif
- {
- ComponentRoutine theRtn;
- ComponentResult result;
-
- theRtn = GetComponentRoutine(params->what); // get address of component routine
-
- if (theRtn == nil) // selector not implemented
- result = badComponentSelector;
- else if (theRtn == kDelegateComponentCall) // selector should be delegated
- result = DelegateComponentCall(params, globals->sourceComponent);
- else
- result = CallComponentFunctionWithStorage((Handle) globals, params, (ComponentFunctionUPP) theRtn);
-
- return (result);
- }
-
- /* =======================================================================================
- GetComponentRoutine
-
- The function of this routine is to return the address of the appropriate component method.
- To do this, the routine must deal with 3 selector ranges:
-
- -5 to -1 These are the standard Component Manager selectors that all components
- must share. Refer to the Component Manager documentation for more info.
-
- 0 to 255 These selectors cannot be delegated. If the sound component does not implement
- one of these selectors, it should return the badComponentSelector error.
-
- 256 to ∞ These selectors should be delegated. If the sound component does not implement
- one of these selectors, it should use DelegateComponentCall() to pass
- this selector on up the chain. If the sound component does implement this
- selector, it should first delegate the selector, then perform the function.
- ======================================================================================= */
-
- ComponentRoutine GetComponentRoutine(short selector)
- {
- void *theRtn;
-
- if (selector < 0)
- switch (selector) // standard component selectors
- {
- case kComponentRegisterSelect:
- theRtn = __ComponentRegister;
- break;
-
- case kComponentVersionSelect:
- theRtn = __ComponentVersion;
- break;
-
- case kComponentCanDoSelect:
- theRtn = __ComponentCanDo;
- break;
-
- case kComponentCloseSelect:
- theRtn = __ComponentClose;
- break;
-
- case kComponentOpenSelect:
- theRtn = __ComponentOpen;
- break;
-
- default:
- theRtn = nil; // unknown selector, so fail
- break;
- }
- else if (selector < kDelegatedSoundComponentSelectors) // selectors that cannot be delegated
- switch (selector)
- {
- case kSoundComponentInitOutputDeviceSelect:
- theRtn = __InitOutputDevice;
- break;
-
- default:
- theRtn = nil; // unknown selector, so fail
- break;
- }
- else // selectors that can be delegated
- switch (selector)
- {
- case kSoundComponentGetInfoSelect:
- theRtn = __GetInfo;
- break;
-
- case kSoundComponentSetInfoSelect:
- theRtn = __SetInfo;
- break;
-
- case kSoundComponentStartSourceSelect:
- theRtn = __StartSource;
- break;
-
- case kSoundComponentPlaySourceBufferSelect:
- theRtn = __PlaySourceBuffer;
- break;
-
- default:
- theRtn = kDelegateComponentCall; // unknown selector, so delegate it
- break;
- }
-
- return (theRtn);
- }
-
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Component Manager Methods
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-
- /* ==============================================================================
- Component Register
-
- This routine is called once, usually at boot time, when the Component Manager
- is first registering this sound component. This routine should check to see if the proper
- hardware is installed and return 0 if it is. If the hardware is not installed,
- the routine should return 1 and this component will not be registered. This is
- also an opportunity to do one-time initializations and perhaps register this
- component again if more than one hardware device is available. Global state information
- can also be saved in the component refcon by calling SetComponentRefCon();
-
- NOTE: The cmpWantsRegisterMessage bit must be set in the component flags of the
- sound component in order for this routine to be called.
- NOTE: This routine is never called at interrupt time.
- ============================================================================== */
-
- pascal ComponentResult __ComponentRegister(void *unused1)
- {
- #pragma unused (unused1)
-
- long result;
- NumVersion version;
-
- if ((Gestalt(gestaltSoundAttr, &result) == noErr) && // snd gestalt is available
- (result & (1L << gestaltSoundIOMgrPresent))) // snd dispatcher is available
- {
- version = SndSoundManagerVersion(); // get the Sound Manager version
- if (version.majorRev >= kRequiredSndMgrMajorRev) // it's what we need
- {
-
- // Check for hardware here. We are always installed, so we return 0
-
- return (0); // install this sound component
- }
- }
-
- return (1); // do not install component
- }
-
- /* ==============================================================================
- Component Version
-
- This routine is called to determine the current version of the sound component.
- It should return a fixed-point value containing the version, like 0x10001
- for version 1.1. The version given here must match the version stored in
- the 'thng' resource.
-
- NOTE: This routine is never called at interrupt time.
- ============================================================================== */
-
- pascal ComponentResult __ComponentVersion(void *unused1)
- {
- #pragma unused (unused1)
-
- return (kNoiseMakerVersion); // return sound component version
- }
-
- /* ==============================================================================
- Component CanDo
-
- This routine is called to determine if a particular selector is implemented.
- It should return 1 if this is a valid selector, or 0 if the selector is not
- implemented or if the selector is always delegated.
-
- NOTE: This routine is never called at interrupt time.
- ============================================================================== */
-
- pascal ComponentResult __ComponentCanDo(void *unused1, short selector)
- {
- #pragma unused (unused1)
-
- ComponentRoutine theRtn;
-
- theRtn = GetComponentRoutine(selector); // see if this selector is implemented
-
- if ((theRtn == nil) || // selector is not implemented
- (theRtn == kDelegateComponentCall)) // or selector is always delegated
- return (0); // no can do
- else
- return (1); // selector is implemented
- }
-
- /* ==============================================================================
- Component Open
-
- This routine is called when the Component Manager creates an instance of this
- component. The routine should allocate global variables in the appropriate heap
- and call SetComponentInstanceStorage() so the Component Manager can remember
- the globals and pass them to all the method calls.
-
- Determining the heap to use can be tricky. The Component Manager will normally
- load the component code into the system heap, which is good, since many applications
- will be sharing this component to play sound. In this case, the components's global
- variable storage should also be created in the system heap.
-
- However, if system heap memory is tight, the Component Manager will load
- the component into the application heap of the first application that plays sound.
- When this happens, the component should create global storage in the application heap
- instead. The Sound Manager will make sure that other applications will not try
- to play sound while the component is in this application heap.
-
- To determine the proper heap to use, call GetComponentInstanceA5(). If the value
- returned is 0, then the component was loaded into the system heap, and all storage
- should be allocated there. If the value returned is non-zero, the component is in
- the application heap specifed by returned A5 value, and all storage should be
- allocated in this application heap.
-
- NOTE: If the component is loaded into the application heap, the value returned by
- GetComponentRefCon() will be 0.
- NOTE: Do not attempt to initialize or access hardware in this call, since the
- Component Manager will call Open() BEFORE calling Register().
- Instead, initialize the hardware during InitOutputDevice(), described below.
- NOTE: This routine is never called at interrupt time.
- ============================================================================== */
-
- pascal ComponentResult __ComponentOpen(void *unused1, ComponentInstance self)
- {
- #pragma unused (unused1)
-
- Handle h;
- GlobalsPtr globals;
- Boolean inSystemHeap;
-
- inSystemHeap = GetComponentInstanceA5(self) == 0; // find out if we were loaded in app heap or system heap
-
- h = NewHandleLockClear(sizeof(GlobalsRecord), inSystemHeap); // get space for globals in appropriate heap
- if (h == nil)
- return(MemError());
-
- globals = (GlobalsPtr) *h;
- SetComponentInstanceStorage (self, (Handle) globals); // save pointer to our globals
-
- globals->self = self; // remember ourselves
- globals->globalsHandle = h; // remember the handle
- globals->inSystemHeap = inSystemHeap; // remember which heap we are in
- globals->prefsHandle = GetPreferences(self, inSystemHeap); // retrieve or create preferences
- if (globals->prefsHandle == nil) // could create preferences
- return (memFullErr); // we can't run
-
- return (noErr);
- }
-
- /* ==============================================================================
- Component Close
-
- This routine is called when the Component Manager is closing the instance of
- this component. If the hardware was initialized, the routine should make sure
- all remaining data is written to the hardware and that the hardware is completely
- turned off. It should delete all global storage and close any other components
- that were opened.
-
- NOTE: Be sure to check that the globals pointer passed in to this routine is
- not set to NIL. If the Open() routine fails for any reason, the Component
- Manager will call this routine passing in a NIL for the globals.
- NOTE: This routine is never called at interrupt time.
- ============================================================================== */
-
- pascal ComponentResult __ComponentClose(GlobalsPtr globals, ComponentInstance self)
- {
- if (globals) // we have some globals
- {
- if (globals->hwInitialized) // hardware was initialized
- {
- ReleaseHardware(globals); // make sure the hardware is off and release it
- HUnlock((Handle) globals->prefsHandle); // let prefs handle roam now
- }
-
- if (globals->sourceComponent) // we opened a mixer
- CloseMixerSoundComponent(globals->sourceComponent); // close it
-
- if (globals->prefsChanged) // preferences changed
- SavePreferences(self, globals->prefsHandle); // save them
-
- if (!globals->inSystemHeap) // prefs are in app heap
- DisposeHandle((Handle) globals->prefsHandle); // dispose of them
-
- DisposeHandle(globals->globalsHandle); // dispose our storage
- }
-
- return (noErr);
- }
-
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Standard Sound Component Methods
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-
- /* ==============================================================================
- InitOutputDevice
-
- This routine is called once when the Sound Manager first opens this sound component.
- The routine should initialize the hardware to default values, allocate the
- appropriate mixer and create any other memory that is required.
-
- NOTE: This routine is never called at interrupt time.
- ============================================================================== */
-
- pascal ComponentResult __InitOutputDevice(GlobalsPtr globals, long actions)
- {
- #pragma unused (actions)
-
- ComponentResult result;
- PreferencesPtr prefsPtr;
-
- // Open the mixer and tell it the type of data it should output. The
- // description includes sample format, sample rate, sample size, number of channels
- // and the size of your optimal interrupt buffer. If a mixer cannot be found that
- // will output this type of data, an error will be returned.
-
- prefsPtr = *globals->prefsHandle; // get settings from preferences
-
- // set to hardware defaults
-
- globals->hwSettings.flags = 0;
- globals->hwSettings.format = (prefsPtr->sampleSize == 8) ? kOffsetBinary : kTwosComplement;
- globals->hwSettings.sampleRate = prefsPtr->sampleRate;
- globals->hwSettings.sampleSize = prefsPtr->sampleSize;
- globals->hwSettings.numChannels = prefsPtr->numChannels;
- globals->hwSettings.sampleCount = kInterruptBufferSamples * 2;
-
- // open mixer that will output this format
-
- result = OpenMixerSoundComponent(&globals->hwSettings, 0, &globals->sourceComponent);
- if (result != noErr)
- return (result);
-
- result = SetupHardware(globals); // setup the hardware to these settings
-
- if (result == noErr)
- {
- globals->hwInitialized = true; // hardware is ready to go
- HLock((Handle) globals->prefsHandle); // lock prefs so we can use them at interrupt time
- }
-
- return (result);
- }
-
- /* ==============================================================================
- GetInfo
-
- This routine returns information about this output component to the Sound Manager.
- A 4-byte OSType selector is used to determine the type and size of the information
- to return. If the component does not support a selector, it should delegate this
- call on up the chain.
-
- NOTE: This can be called at interrupt time. However, selectors that return
- a handle will not be called at interrupt time.
- ============================================================================== */
-
- pascal ComponentResult __GetInfo(GlobalsPtr globals, SoundSource sourceID,
- OSType selector, void *infoPtr)
- {
- HandleListPtr listPtr;
- short *sp;
- UnsignedFixed *lp;
- Handle h;
- PreferencesPtr prefsPtr;
- ComponentResult result = noErr;
-
- prefsPtr = *globals->prefsHandle; // get settings from preferences
-
- switch (selector)
- {
- case siSampleSize: // return current sample size
- *((short *) infoPtr) = prefsPtr->sampleSize;
- break;
-
- case siSampleSizeAvailable: // return samples sizes available
- h = NewHandle(sizeof(short) * kSampleSizesCount); // space for sample sizes
- if (h == nil)
- return (MemError());
-
- listPtr = (HandleListPtr) infoPtr;
- listPtr->count = kSampleSizesCount; // no. sample sizes in handle
- listPtr->handle = h; // handle to be returned
-
- sp = (short *) *h; // store sample sizes in handle
- *sp++ = k8BitSamples;
- *sp++ = k16BitSamples;
- break;
-
- case siSampleRate: // return current sample rate
- *((Fixed *) infoPtr) = prefsPtr->sampleRate;
- break;
-
- case siSampleRateAvailable: // return sample rates available
- h = NewHandle(sizeof(UnsignedFixed) * kSampleRatesCount); // space for sample rates
- if (h == nil)
- return (MemError());
-
- listPtr = (HandleListPtr) infoPtr;
- listPtr->count = kSampleRatesCount; // no. sample rates in handle
- listPtr->handle = h; // handle to be returned
-
- lp = (UnsignedFixed *) *h;
-
- // If the hardware can support a range of sample rate values, then the
- // list count should be set to zero and the min and max sample rate values
- // should be stored in the handle.
-
- if (kSupportsSampleRateRange)
- {
- listPtr->count = 0;
- *lp++ = kSampleRateMin; // min
- *lp++ = kSampleRateMax; // max
- }
-
- // If the hardware supports a limited set of sample rates, then the list count
- // should be set to the number of sample rates and this list of rates should be
- // stored in the handle.
-
- else
- {
- *lp++ = kSampleRate11; // store sample rates in handle
- *lp++ = kSampleRate22;
- *lp++ = kSampleRate44;
- }
- break;
-
- case siNumberChannels: // return current no. channels
- *((short *) infoPtr) = prefsPtr->numChannels;
- break;
-
- case siChannelAvailable: // return channels available
- h = NewHandle(sizeof(short) * kChannelsCount); // space for channels
- if (h == nil)
- return (MemError());
-
- listPtr = (HandleListPtr) infoPtr;
- listPtr->count = kChannelsCount; // no. channels in handle
- listPtr->handle = h; // handle to be returned
-
- sp = (short *) *h; // store channels in handle
- *sp++ = kNumChannelsMono;
- *sp++ = kNumChannelsStereo;
- break;
-
- case siHardwareVolume:
- *((long *)infoPtr) = prefsPtr->volume;
- break;
-
- // if you do not handle this selector, then delegate it up the chain
- default:
- result = SoundComponentGetInfo(globals->sourceComponent, sourceID, selector, infoPtr);
- break;
-
- }
-
- return (result);
- }
-
- /* ==============================================================================
- SetInfo
-
- This routine sets information about this component. A 4-byte OSType selector is
- used to determine the type and size of the information to apply. If the component
- does not support a selector, it should delegate this call on up the chain.
-
- NOTE: This can be called at interrupt time.
- ============================================================================== */
-
- pascal ComponentResult __SetInfo(GlobalsPtr globals, SoundSource sourceID,
- OSType selector, void *infoPtr)
- {
- PreferencesPtr prefsPtr;
- ComponentResult result = noErr;
-
- prefsPtr = *globals->prefsHandle; // get settings from preferences
-
- switch (selector)
- {
- case siSampleSize: // set sample size
- {
- short sampleSize = (short) infoPtr;
-
- if ((sampleSize == k8BitSamples) || // make sure it is a valid sample size
- (sampleSize == k16BitSamples))
- {
- prefsPtr->sampleSize = sampleSize; // save new size in prefs
- globals->prefsChanged = true; // save prefs on close
- }
- else
- result = siInvalidSampleSize;
- break;
- }
-
- case siSampleRate: // set sample rate
- {
- UnsignedFixed sampleRate = (UnsignedFixed) infoPtr;
-
- if (kSupportsSampleRateRange) // sample rate range
- {
- if ((kSampleRateMin <= sampleRate) && (sampleRate <= kSampleRateMax)) // make sure it is a valid sample rate
- {
- prefsPtr->sampleRate = sampleRate; // save new rate in prefs
- globals->prefsChanged = true; // save prefs on close
- }
- else
- result = siInvalidSampleRate;
- }
- else
- {
- if ((sampleRate == kSampleRate11) || // make sure it is a valid sample rate
- (sampleRate == kSampleRate22) ||
- (sampleRate == kSampleRate44))
- {
- prefsPtr->sampleRate = sampleRate; // save new rate in prefs
- globals->prefsChanged = true; // save prefs on close
- }
- else
- result = siInvalidSampleSize;
- }
- break;
- }
-
- case siNumberChannels: // set no. channels
- {
- short numChannels = (short) infoPtr;
-
- if ((numChannels == kNumChannelsMono) || // make sure it is a valid no. channels
- (numChannels == kNumChannelsStereo))
- {
- prefsPtr->numChannels = numChannels; // save new num channels in prefs
- globals->prefsChanged = true; // save prefs on close
- }
- else
- result = notEnoughHardware;
- break;
- }
-
- case siHardwareVolume:
- prefsPtr->volume = (long) infoPtr; // save volume in prefs
- globals->prefsChanged = true; // save prefs on close
- result = SetHardwareVolume(globals, prefsPtr->volume);
- break;
-
- // if you do not handle this selector, then call up the chain
- default:
- result = SoundComponentSetInfo(globals->sourceComponent, sourceID, selector, infoPtr);
- break;
- }
-
- return (result);
- }
-
- /* ==============================================================================
- StartSource
-
- This routine is used to start sounds playing that are currently paused. It should
- first delegate this call up the chain so the rest of the chain can prepare
- to play this sound. Then, if the hardware is not already started it should be
- turned on.
-
- NOTE: This can be called at interrupt time.
- ============================================================================== */
-
- pascal ComponentResult __StartSource(GlobalsPtr globals, short count, SoundSource *sources)
- {
- ComponentResult result;
-
- // tell the mixer to start these sources
- result = SoundComponentStartSource(globals->sourceComponent, count, sources);
- if (result != noErr)
- return (result);
-
- // make sure hardware interrupts are running
- result = StartHardware(globals);
-
- return (result);
- }
-
- /* ==============================================================================
- PlaySourceBuffer
-
- This routine is used to specify a new sound to play and conditionally start
- the hardware playing that sound.It should first delegate this call up the
- chain so the rest of the chain can prepare to play this sound. Then, if the
- hardware is not already started it should be turned on.
-
- NOTE: This can be called at interrupt time.
- ============================================================================== */
-
- pascal ComponentResult __PlaySourceBuffer(GlobalsPtr globals, SoundSource sourceID, SoundParamBlockPtr pb, long actions)
- {
- ComponentResult result;
-
- // tell mixer to start playing this new buffer
- result = SoundComponentPlaySourceBuffer(globals->sourceComponent, sourceID, pb, actions);
- if (result != noErr)
- return (result);
-
- // if the kSourcePaused bit is set, then do not turn on your hardware just yet
- // (the assumption is that StartSource() will later be used to start this sound playing).
- // If this bit is not set, turn your hardware interrupts on.
-
- if (!(actions & kSourcePaused))
- result = StartHardware(globals);
-
- return (result);
- }
-
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // This routine creates a locked handle in the requested heap.
-
- Handle NewHandleLockClear(long len, Boolean inSystemHeap) // allocate a new handle, lock it down and clear it
- {
- Handle h;
-
- if (inSystemHeap) // we are loaded into the system heap
- {
- ReserveMemSys(len); // create it low down in system heap
- h = NewHandleSysClear(len);
- }
- else // we are loaded into the app heap
- {
- h = NewHandleClear(len); // create our memory there and move it out of the way
- if (h) MoveHHi(h);
- }
-
- if (h) HLock(h); // lock it down
- return (h);
- }
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // This routine gets the preferences from the refCon or the preferences file
- // or creates default preferences if it has to.
-
- PreferencesHandle GetPreferences(ComponentInstance self, Boolean inSystemHeap)
- {
- Handle prefsHandle, componentName;
- PreferencesPtr prefsPtr;
- ComponentDescription componentDesc;
- OSErr err;
-
- prefsHandle = (Handle) GetComponentRefcon((Component) self); // get prefs from component refcon
- if (prefsHandle)
- return ((PreferencesHandle) prefsHandle); // valid prefs in refcon, so we are done
-
- componentName = NewHandle(0); // make space for name
- if (componentName == nil)
- goto NewNameHandleFailed;
-
- err = GetComponentInfo((Component) self, &componentDesc, componentName, nil, nil); // get component name and subtype
- if (err != noErr)
- goto InfoFailed;
-
- prefsHandle = NewHandleLockClear(sizeof(PreferencesRecord), inSystemHeap); // create space for prefs handle
- if (prefsHandle == nil)
- goto NewPrefsHandleFailed;
-
- HUnlock(prefsHandle); // don't leave prefs handle locked down forever
- HLock(componentName);
-
- err = GetSoundPreference(componentDesc.componentSubType, (StringPtr) *componentName, prefsHandle); // get prefs handle from file
- if (err != noErr) // no file or prefs not in file
- goto GetPrefsFailed;
-
- if (GetHandleSize(prefsHandle) != sizeof(PreferencesRecord)) // older version of preferences
- goto GetPrefsFailed; // start with all new preferences
-
- DisposeHandle(componentName);
- SetComponentRefcon((Component) self, (long) prefsHandle); // save prefs in refcon
-
- return ((PreferencesHandle) prefsHandle); // we are done
-
-
- /* If we end up here, it means that the preferences could not be loaded out of the
- sound preferences file for some reason, so we need to generate some default preferences. */
-
- GetPrefsFailed:
- DisposeHandle(prefsHandle);
- NewPrefsHandleFailed:
- InfoFailed:
- DisposeHandle(componentName);
- NewNameHandleFailed:
-
- prefsHandle = NewHandleLockClear(sizeof(PreferencesRecord), inSystemHeap); // create space for prefs handle
- if (prefsHandle == nil)
- return (nil);
-
- HUnlock(prefsHandle); // don't leave prefs handle locked down forever
- prefsPtr = (PreferencesPtr) *prefsHandle;
-
- prefsPtr->sampleSize = k8BitSamples; // initialize prefs to defaults
- prefsPtr->sampleRate = kSampleRate22;
- prefsPtr->numChannels = kNumChannelsStereo;
- prefsPtr->volume = (kFullVolume << 16) | kFullVolume;
-
- SetComponentRefcon((Component) self, (long) prefsHandle); // save prefs in refcon
-
- return ((PreferencesHandle) prefsHandle); // we are done
- }
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // This routine saves the hardware globals in the preferences file
-
- void SavePreferences(ComponentInstance self, PreferencesHandle prefsHandle)
- {
- Handle componentName;
- ComponentDescription componentDesc;
- OSErr err;
-
- componentName = NewHandle(0); // make space for name
- if (componentName == nil)
- goto NewNameHandleFailed;
-
- err = GetComponentInfo((Component) self, &componentDesc, componentName, nil, nil); // get component name and subtype
- if (err != noErr)
- goto InfoFailed;
-
- HLock(componentName);
- err = SetSoundPreference(componentDesc.componentSubType, (StringPtr) *componentName, (Handle) prefsHandle);
-
- InfoFailed:
- DisposeHandle(componentName);
- NewNameHandleFailed:
-
- return;
- }
-
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Hardware-specific Methods
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Initialize the hardware. Our "hardware" just uses the Sound Manager to
- // play sounds. Your hardware would probably do something completely different.
- //
- // We have to find another output component to play sound with, or the Sound Manager
- // will try to use our component by default when we call SndNewChannel. We do this
- // by searching for another output component, and passing that component to SndNewChannel
- // with a flag set telling the Sound Manager to use this component instead of the default.
-
-
- OSErr SetupHardware(GlobalsPtr globals)
- {
- ComponentDescription searching, found;
- OSType selfSubType;
- Component sndComponent;
- OSErr err;
-
- // find our subtype
-
- err = GetComponentInfo((Component) globals->self, &found, nil, nil, nil); // get component subtype
- if (err != noErr)
- return (err);
-
- selfSubType = found.componentSubType; // save it for later
-
- // search for another output component that can play sounds
-
- searching.componentType = kSoundHardwareType;
- searching.componentSubType = kAnyComponentSubType;
- searching.componentManufacturer = kAnyComponentManufacturer;
- searching.componentFlags = 0;
- searching.componentFlagsMask = kAnyComponentFlagsMask;
-
- sndComponent = nil;
- while (sndComponent = FindNextComponent(sndComponent, &searching))
- {
- err = GetComponentInfo(sndComponent, &found, nil, nil, nil); // get component subtype
- if (err != noErr)
- return (err);
-
- if (found.componentSubType != selfSubType) // found something besides ourself
- break;
- }
-
- if (sndComponent == nil) // no other output components available
- return (notEnoughHardware); // bail
-
- // play sounds out this component
-
- globals->sndChan = nil;
- err = SndNewChannel(&globals->sndChan, kUseOptionalOutputDevice, (long) sndComponent, InterruptRoutine);
-
- return (err);
- }
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Release the hardware.
-
- void ReleaseHardware(GlobalsPtr globals)
- {
- StopHardware(globals); // make sure hardware is off
-
- SndDisposeChannel(globals->sndChan, true); // release sound channel
- }
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Turn on hardware interrupts.
-
- OSErr StartHardware(GlobalsPtr globals)
- {
- OSErr err = noErr;
-
- if (!globals->hwInterruptsOn)
- {
- SndCommand cmd;
-
- globals->hwInterruptsOn = true; // the hardware will soon be on
-
- // Turn hardware on here.
-
- cmd.cmd = callBackCmd; // we just queue up a callback to start interrupts
- cmd.param1 = 0;
- cmd.param2 = (long) globals;
-
- err = SndDoCommand(globals->sndChan, &cmd, true); // call the interrupt routine
- }
-
- return (err);
- }
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Turn off hardware interrupts.
-
- void StopHardware(GlobalsPtr globals)
- {
- if (globals->hwInterruptsOn)
- {
- SndCommand cmd;
-
- // Turn hardware off here.
-
- cmd.cmd = quietCmd;
- cmd.param1 = 0;
- cmd.param2 = 0;
-
- SndDoImmediate(globals->sndChan, &cmd); // stop sound playback
-
- globals->hwInterruptsOn = false; // the hardware is now off
- }
- }
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Temporarily suspend hardware interrupts, so the interrupt routine can work in peace.
-
- void SuspendHardware(GlobalsPtr globals)
- {
- if (globals->hwInterruptsOn)
- {
- // Suspend hardware interrupts here
- }
- }
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Resume hardware interrupts after they were suspended.
-
- void ResumeHardware(GlobalsPtr globals)
- {
- if (globals->hwInterruptsOn)
- {
- SndCommand cmd;
-
- // Turn hardware on here.
-
- cmd.cmd = callBackCmd; // we just queue up a callback to resume interrupts
- cmd.param1 = 0;
- cmd.param2 = (long) globals;
-
- SndDoCommand(globals->sndChan, &cmd, true); // call the interrupt routine
- }
- }
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Set hardware volume.
-
- OSErr SetHardwareVolume(GlobalsPtr globals, unsigned long volume)
- {
- SndCommand cmd;
- OSErr err;
-
- cmd.cmd = volumeCmd;
- cmd.param1 = 0;
- cmd.param2 = volume;
-
- err = SndDoImmediate(globals->sndChan, &cmd); // set volume for this channel
-
- return (err);
- }
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // This routine is called every hardware interrupt to fill the hardware
- // with audio data. First it should suspend interrupts so it will not
- // be interrupted again. Then it should get more data from the source mixer,
- // and copy the data to the hardware. On the way out, if all data was copied,
- // try to get some more so it will be available immediately next interrupt.
-
- pascal void InterruptRoutine(SndChannelPtr chan, SndCommand *cmd)
- {
- #pragma unused (chan)
-
- GlobalsPtr globals;
- SoundComponentDataPtr sourceDataPtr;
-
- globals = (GlobalsPtr) cmd->param2; // get globals from command
-
- SuspendHardware(globals); // suspend interrupts while we are processing
-
- sourceDataPtr = GetMoreSource(globals); // get source from mixer
- if (sourceDataPtr == nil) // no more source
- {
- StopHardware(globals); // turn hardware off
- return;
- }
-
- CopySamplesToHardware(globals, sourceDataPtr); // fulfill hardware request
-
- // Normally, you will want to check to see if you have run out
- // of data here and get more right away so you will be ready for
- // the next interrupt. This example does not have any hardware
- // to copy the data to, so it leaves the data in the mixer buffer and
- // thus cannot call for more until it has been played.
-
- // if (sourceDataPtr->sampleCount == 0) // exhausted the source
- // sourceDataPtr = GetMoreSource(globals); // get more for next time
-
- ResumeHardware(globals); // resume interrupts
- }
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // This routine returns the data pointer to your mixer source. If there
- // is no source or it is empty, it will call down the chain to fill it up.
-
- SoundComponentDataPtr GetMoreSource(GlobalsPtr globals)
- {
- ComponentResult result;
- SoundComponentDataPtr sourceDataPtr = globals->sourceDataPtr;
-
- if ((sourceDataPtr == nil) || (sourceDataPtr->sampleCount == 0)) // no data - better get some
- {
- result = SoundComponentGetSourceData(globals->sourceComponent, &globals->sourceDataPtr);
- sourceDataPtr = globals->sourceDataPtr;
-
- if ((result != noErr) || // error getting data
- (sourceDataPtr == nil) || // source has no data pointer to return
- (sourceDataPtr->sampleCount == 0)) // source has no more data
- {
- return (nil); // stop the presses
- }
- }
-
- return (sourceDataPtr); // return pointer to source
- }
-
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Example-specific Methods
- //
- // These routines are only needed to make this example work and as such should not be
- // used in your sound component.
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // This routine copies the data returned by the mixer to the hardware.
-
- void CopySamplesToHardware(GlobalsPtr globals, SoundComponentDataPtr sourceDataPtr)
- {
- SndCommand cmd;
- ExtSoundHeader sndHeader;
- OSErr err;
-
- sndHeader.samplePtr = (Ptr) sourceDataPtr->buffer; // buffer to play
- sndHeader.numChannels = sourceDataPtr->numChannels; // number of channels
- sndHeader.sampleRate = sourceDataPtr->sampleRate; // sample rate
- sndHeader.loopStart = 0; // no loop points
- sndHeader.loopEnd = 0;
- sndHeader.encode = extSH; // this is an extended header
- sndHeader.baseFrequency = 60; // base note
- sndHeader.numFrames = sourceDataPtr->sampleCount; // length in samples
- sndHeader.markerChunk = 0; // sync track
- sndHeader.instrumentChunks = 0; // AIFF instrument chunks
- sndHeader.AESRecording = 0;
- sndHeader.sampleSize = sourceDataPtr->sampleSize; // number of bits in sample
- sndHeader.futureUse1 = 0; // reserved by Apple
- sndHeader.futureUse2 = 0; // reserved by Apple
- sndHeader.futureUse3 = 0; // reserved by Apple
- sndHeader.futureUse4 = 0; // reserved by Apple
-
- cmd.cmd = bufferCmd;
- cmd.param1 = 0;
- cmd.param2 = (long) &sndHeader;
-
- err = SndDoImmediate(globals->sndChan, &cmd); // play this sound
-
- sourceDataPtr->sampleCount = 0; // sound has been played
- }
-